package com.chushouya.manager.service.admin.impl;

import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.chushouya.manager.config.OssProperties;
import com.chushouya.manager.dto.admin.oss.OssStsDTO;
import com.chushouya.manager.service.admin.OssService;
import com.general.framework.core.exception.Ex;
import com.general.framework.core.lang.Check;
import com.general.framework.core.lang.Jsons;
import com.general.framework.core.lang.Numbers;
import com.general.framework.core.lang.Strings;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;

/**
 * OSS存储服务
 */
@Slf4j
@Service
public class OssServiceImpl implements OssService {

    @Resource
    private OssProperties ossProperties;

    private OSS ossClient;

    @PostConstruct
    public void init() {
        this.ossClient = getOssClient();
    }

    @PreDestroy
    public void preDestroy() {
        if (ossClient != null) {
            ossClient.shutdown();
        }
    }

    @Override
    public String uploadImage(final InputStream inputStream, final String filePath, final String contentType) {
        Check.hasText(filePath, "文件路径不能为空");
        return this.uploadFile(inputStream, filePath, contentType);
    }

    @Override
    public String uploadFile(final byte[] bytes, final String filePath, final String contentType) {
        final ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
        return this.uploadFile(inputStream, filePath, contentType);
    }

    @Override
    public String uploadFile(final InputStream inputStream, final String filePath, final String contentType) {
        final ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType(contentType);

        final PutObjectRequest putObjectRequest = new PutObjectRequest(ossProperties.getBucket(), filePath, inputStream);
        putObjectRequest.setMetadata(metadata);
        final PutObjectResult objectResult = ossClient.putObject(putObjectRequest);
        // 全路径
        return ossProperties.getHost().concat(filePath);
    }

    @Override
    public OssStsDTO getOssSts(String basePath) {
        // Init ACS Client
        String regionId = ossProperties.getRegionId();
        final IClientProfile profile = DefaultProfile.getProfile(regionId, ossProperties.getAccessKeyId(), ossProperties.getAccessKeySecret());
        IAcsClient client = new DefaultAcsClient(profile);
        //构造请求，设置参数。

        AssumeRoleRequest request = new AssumeRoleRequest();
        request.setRoleArn(ossProperties.getRoleArn());
        request.setRoleSessionName(ossProperties.getRoleSessionName());
        request.setPolicy(ossProperties.getPolicy());
        request.setDurationSeconds(ossProperties.getDurationSeconds());

        try {
            final AssumeRoleResponse response = client.getAcsResponse(request);
            log.debug("获取OSS授权访问信息success,roleUser={}", Jsons.toJsonString(response.getAssumedRoleUser()));
            final OssStsDTO ossSts = new OssStsDTO();
            ossSts.setEndpoint(ossProperties.getEndpoint());
            ossSts.setAccessKeyId(response.getCredentials().getAccessKeyId());
            ossSts.setAccessKeySecret(response.getCredentials().getAccessKeySecret());
            ossSts.setSecurityToken(response.getCredentials().getSecurityToken());
            ossSts.setExpiration(response.getCredentials().getExpiration());
            ossSts.setHost(ossProperties.getHost());
            ossSts.setBucket(ossProperties.getBucket());
            
            String finalBasePath;
            if (Strings.isNotBlank(basePath)) {
                finalBasePath = basePath;
                ossSts.setBasePath(basePath);
            } else {
                finalBasePath = "upload";
                ossSts.setBasePath("upload");
            }
            ossSts.setRegionId(regionId);
            
            // 生成 OSS4-HMAC-SHA256 签名相关字段
            String tempAccessKeyId = response.getCredentials().getAccessKeyId();
            String tempAccessKeySecret = response.getCredentials().getAccessKeySecret();
            String securityToken = response.getCredentials().getSecurityToken();
            
            // 设置过期时间（当前时间 + 1小时）
            long expireTime = System.currentTimeMillis() / 1000 + 3600;
            
            // 生成日期字符串（使用 UTC 时间）
            java.time.ZonedDateTime now = java.time.ZonedDateTime.now(java.time.ZoneOffset.UTC);
            String dateStr = now.format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"));
            String dateTimeStr = now.format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
            
            // 构建策略（使用正确的 ISO8601 格式）
            String expirationTime = java.time.Instant.ofEpochSecond(expireTime).toString();
            String policyJson = String.format(
                "{\"expiration\":\"%s\",\"conditions\":[[\"starts-with\",\"$key\",\"%s/\"]]}",
                expirationTime,
                finalBasePath
            );
            String policy = java.util.Base64.getEncoder().encodeToString(policyJson.getBytes());
            
            // 生成签名（对 Base64 编码的策略进行签名）
            String signature = generateSignature(tempAccessKeySecret, policy);
            
            // 设置基本字段（标准表单上传）
            ossSts.setAccessid(tempAccessKeyId);
            ossSts.setPolicy(policy);
            ossSts.setSignature(signature);
            ossSts.setExpire(expireTime);
            ossSts.setDir(finalBasePath + "/" + dateStr + "/");
            
            // 设置 STS 安全令牌
            ossSts.setXOssSecurityToken(securityToken);
            
            // 构建回调参数
            String callbackUrl = ossProperties.getCallbackUrl();
            
            // 回调参数
            String callbackParamJson = String.format(
                "{\"callbackUrl\":\"%s\",\"callbackBody\":\"filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}&format=${imageInfo.format}\",\"callbackBodyType\":\"application/x-www-form-urlencoded\"}",
                callbackUrl
            );
            
            // Base64编码回调参数
            String base64CallbackBody = java.util.Base64.getEncoder().encodeToString(callbackParamJson.getBytes());
            
            // 将回调参数添加到返回结果中
            ossSts.setCallback(base64CallbackBody);
            
            return ossSts;
        } catch (ClientException e) {
            log.error("获取OSS授权访问信息error", e);
            throw Ex.systemError();
        }
    }

    /**
     * 获取ossClient
     */
    private OSS getOssClient() {
        final String endpoint = ossProperties.getEndpoint();
        final String accessKeyId = ossProperties.getAccessKeyId();
        final String accessKeySecret = ossProperties.getAccessKeySecret();

        final ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
        // 设置OSSClient允许打开的最大HTTP连接数，默认为1024个。
        conf.setMaxConnections(500);
        // 请求失败后最大的重试次数
        conf.setMaxErrorRetry(Numbers.INTEGER_FIVE);
        return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, conf);
    }

    /**
     * 生成签名
     */
    private String generateSignature(String accessKeySecret, String stringToSign) {
        try {
            javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA1");
            javax.crypto.spec.SecretKeySpec secretKeySpec = new javax.crypto.spec.SecretKeySpec(
                accessKeySecret.getBytes("UTF-8"), "HmacSHA1");
            mac.init(secretKeySpec);
            byte[] signatureBytes = mac.doFinal(stringToSign.getBytes("UTF-8"));
            return java.util.Base64.getEncoder().encodeToString(signatureBytes);
        } catch (Exception e) {
            log.error("生成签名失败", e);
            throw new RuntimeException("生成签名失败", e);
        }
    }
}
